Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link field type #15251

Merged
merged 22 commits into from
Jul 1, 2024
Merged

Link field type #15251

merged 22 commits into from
Jul 1, 2024

Conversation

brandonkelly
Copy link
Member

@brandonkelly brandonkelly commented Jun 25, 2024

Description

Adds a new “Link” field type, which replaces the former “URL” field type.

Link fields feature a simple UI for defining a link, of one of the allowed types the field was configured with:

  • URL
  • Asset
  • Category
  • Email
  • Entry
  • Phone
Three Link fields. The first is configured with only the “URL” link type, and has a link to https://craftcms.com filled in. The second is configured with multiple link types, and has “URL” selected, set to https://console.craftcms.com. The third has “Entry” selected, and an entry titled “What’s on Tap” is selected.

Textual link types automatically set valid values to hyperlinks on blur, or when the Esc key is pressed. Hyperlinks are automatically replaced with text inputs when the input container is clicked on, or the “Edit” disclosure menu option is chosen.

Templating/GraphQL

Regardless of the link type selected, field values will return the full, rendered URL.

# GraphQL query
query {
  entry(id: 99) {
    ...on link_Entry {
      link1
      link2
      link3
    }
  }
}
// Response
{
  "data": {
    "entry": {
      "link1": "https://craftcms.com",
      "link2": "https://console.craftcms.com",
      "link3": "https://craft5.ddev.site/work"
    }
  }
}

The following properties are also available off of the value in Twig templates:

  • type – The link type’s ID (url, category, email, entry, or tel)
  • label – A suggested label that could be used in anchor links (e.g. the URL sans-schema, or the element’s title)
  • link – An anchor tag made up of the link URL and label
  • element – The linked element, if applicable
{% if entry.myLinkField.element %}
  {{ entry.myLinkField.link }}
{% else %}
  <a href="{{ entry.myLinkField }}" target="_blank" rel="noopener">
    {{ entry.myLinkField.label }}
  </a>
{% endif %}

Registering custom link types

Additional link types can be registered via the EVENT_REGISTER_LINK_TYPES event. Link types are defined as classes which extend one of the following base classes:

  • craft\fields\linktypes\BaseLinkType
  • craft\fields\linktypes\BaseElementLinkType (for element link types)
  • craft\fields\linktypes\BaseTextLinkType (for textual link types)

Here’s a how to register a link type for a custom element type:

// Plugin.php
use craft\events\RegisterComponentTypesEvent;
use craft\fields\Link;
use modules\foo\AssetLinkType;
use yii\base\Event;

Event::on(
    Link::class,
    Link::EVENT_REGISTER_LINK_TYPES,
    function(RegisterComponentTypesEvent $event
) {
    $event->types[] = MyElementLinkType::class;
});
// MyElementLinkType.php
use craft\fields\linktypes\BaseElementLinkType;

abstract class MyElementLinkType extends BaseElementLinkType
{
    protected static function elementType(): string
    {
        return MyElementType::class;
    }

    protected function availableSourceKeys(): array
    {
        return [
            // list the source keys that contain elements that have URLs
            // ...
        ];
    }

    protected function selectionCriteria(): array
    {
        return array_merge(parent::selectionCriteria(), [
            // ...
        ]);
    }
}

Notes

  • craft\fields\Url has been aliased to craft\fields\Link, so existing URL fields will be switched to Link automatically, and any code references to Url will continue to work.
  • Link fields do not have the ability to show settings to control target, label, etc. Those can be captured via separate custom fields.

Related issues

Copy link

linear bot commented Jun 25, 2024

CMS-1279 Link field

@brandonkelly brandonkelly requested a review from gcamacho079 June 25, 2024 22:47
@mmikkel
Copy link
Contributor

mmikkel commented Jun 25, 2024

This is awesome, thanks a lot Brandon.

Pretty much the only thing I'd hope to see added to this, is some way of accessing the actual element selected, in links using the BaseElementLinkType class.

While I think it's fine that Link fields doesn't expose an input for labels/link text, it'd be great (and would eliminate some AX friction) if it was possible to do something like this, in order to have element links' texts/labels default to the selected element's title:

{% if entry.myLinkField.type == 'element-entry' %}
  <a href="{{ entry.myLinkField }}">{{ entry.myLinkLabelField | default(entry.myLinkField.element.title) }}</a>
{% else %}
  <a href="{{ entry.myLinkField }}" target="_blank" rel="noopener">
    {{ entry.myLinkLabelField }}
  </a>
{% endif %}

Alternatively, instead of exposing the element for BaseElementLinkType via something like myLinkField.element, maybe the BaseLinkType class could implement something like a getText() method, that returns the value (i.e. URL) for the base class, the selected element's title for the BaseElementLinkType, and can be overridden to return whatever for custom link types.

@BenParizek
Copy link
Contributor

So glad to finally see this and a way to register custom Elements Link Types.

Links started the internet! They will feel so at home in a CMS.

@leevigraham
Copy link
Contributor

Link fields do not have the ability to show settings to control target, label, etc. Those can be captured via separate custom fields.

I assume you're thinking multiple links would be a matrix field with a block that contains the link field and the overrides.

@brandonkelly
Copy link
Member Author

@leevigraham That or something like #14600.

@brandonkelly
Copy link
Member Author

brandonkelly commented Jun 26, 2024

@mmikkel Both good ideas, so just added them both. Updated the PR description with:

The following properties are also available off of the value in Twig templates:

  • type – The link type’s ID (url, category, email, entry, or tel)
  • label – A suggested label that could be used in anchor links (e.g. the URL sans-schema, or the element’s title)
  • link – An anchor tag made up of the link URL and label
  • element – The linked element, if applicable
{% if entry.myLinkField.element %}
  {{ entry.myLinkField.link }}
{% else %}
  <a href="{{ entry.myLinkField }}" target="_blank" rel="noopener">
    {{ entry.myLinkField.label }}
  </a>
{% endif %}

@MoritzLost
Copy link
Contributor

@brandonkelly Very good addition 🎉

One thing we have used a lot with Typed Link Field and now Hyper is the ability to append something (a fragment or query parameters) to entry links. For example, you want to link to a specific section on a specific entry, so you select the entry and enter the fragment for the section on that page. Or you want to link to a search page with pre-selected filters or a search term, in this case you would enter an additional query string. Would it be possible to support this for element links?

There's still a use-case for Hyper, which supports full field layouts and creating multiple links in one field. But appending something to entry URLs is much more common, so this would be nice to have as a core feature.

@Mosnar
Copy link
Contributor

Mosnar commented Jun 26, 2024

This is awesome and much appreciated! I don't see it replacing Hyper (yet?), but at the very least it will solve for a lot of basic scenarios. Thank you for listening to community feedback and getting out a workable solution instead of waiting for multi-element-type sources. ❤️

@brandonkelly
Copy link
Member Author

@MoritzLost I don’t see that feature in Hyper. You must be capturing the fragment with a custom field?

@MoritzLost
Copy link
Contributor

@MoritzLost I don’t see that feature in Hyper. You must be capturing the fragment with a custom field?

@brandonkelly No, it's a native attribute (urlSuffix) in Hyper (mentioned here in passing). It's not placed in the field layout by default for new fields, but can be added to the field layout for every link type:

hyper-link-suffix

In Typed Link Field it's an option for every link type, this results in an additional text input next to the URL / entry selector:

typed-link-field-url-suffix

@brandonkelly
Copy link
Member Author

@MoritzLost Gotcha… still, that could pretty easily be added with an adjacent custom field, just like “Open in a new window” etc.

@droemmelbert
Copy link

@brandonkelly Would it also make sense to include linking to assets? At least that is a feature that I would be using quite a bit when a button links to a PDF file.

@brandonkelly
Copy link
Member Author

@droemmelbert I decided not to initially because it seemed unlikely that there’s a need to link to assets or a webpage URL; more likely you’d just want a dedicated field just for linking to an asset, at which point you’d be better off using an Assets field, which would be much more specialized than Link (fine-tuned control over available sources, file types, permission enforcement, plus direct-to-field upload support).

If I’m wrong and there is a legitimate need for a field that links to both webpage URLs and assets, I can consider pulling those features in.

@Mosnar
Copy link
Contributor

Mosnar commented Jun 26, 2024

@droemmelbert I decided not to initially because it seemed unlikely that there’s a need to link to assets or a webpage URL; more likely you’d just want a dedicated field just for linking to an asset, at which point you’d be better off using an Assets field, which would be much more specialized than Link (fine-tuned control over available sources, file types, permission enforcement, plus direct-to-field upload support).

If I’m wrong and there is a legitimate need for a field that links to both webpage URLs and assets, I can consider pulling those features in.

I think it’s worth including. Consider a multi-purpose component such as a button in the hero or a CTA on a page. Sometimes those get used to link to forms, sometimes they get linked to downloadables, such as PDFs. I typically enable it as an option by default to prevent people from pasting a URL directly to an asset rather than asking a developer to change it.

@mmikkel
Copy link
Contributor

mmikkel commented Jun 26, 2024

I also think assets are definitely worth including. FWIW, clients link to assets in CKEditor fields all the time, e.g. to create links for downloading brand materials, etc.

@MattWilcox
Copy link

Honestly, the "link" field is almost always a "button" field, and yup... clients "link to" assets, so I'd be down for Assets as well.

Copy link
Contributor

@gcamacho079 gcamacho079 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The input element doesn’t have an accessible name. I think we previously used “Value” as the accessible name. This is also the case when only one URL type is allowed, and only the input is present.
  • Depending on the input value’s size, the link generated may not meet touch target size requirements. This is an edge case, but adding additional space between the link and the Actions button would help provide a nice touch target size. (To test, type a short value like “hi”, move the focus from the input, and run an axe DevTools scan.)
  • It may be helpful (in terms of planned accessibility updates) to visually separate the select and the input versus nesting the select inside the border. Specifically, the presentation will have to be adjusted when we darken input borders globally and give fields inside of fieldsets distinct labels. Or, we can use the Link field to introduce distinct labels for fields inside fieldsets (PT-1358, PT-508).
  • As with the above issue, turning the input value into a link will require extra a11y/UX considerations when we update the UI for contrast and persistent, visible labels. I suggest keeping the input as an input and adding a separate preview link next to or below the field.
  • When choosing or removing entries or categories, keyboard focus is dropped. After making a selection from an element selector modal, focus should move to the chip link or actions button. When removing a selection, focus should move back to the “Choose” button

A couple of issues above have me wondering if we simplify the UI to retain the fieldset layout regardless of how many URL types are allowed. If only one URL type is permitted, disable the “URL type” option.

# Conflicts:
#	src/web/assets/cp/dist/cp.js
#	src/web/assets/cp/dist/cp.js.map
Stays consistent with URL fields
@brandonkelly
Copy link
Member Author

Asset link type option added 🎉

Required some refactoring—link types get instantiated now, and can define settings. All element type-based link types now show a “Sources” setting, allowing you to fine tune which sources should be available in element selection modals.

The asset link type also has “Allowed File Types”, “Show unpermitted volumes”, and “Show unpermitted files” settings, similar to Assets fields.

Asset link type settings

Uploading directly to the field isn’t supported, as that would add a huge amount of complexity and be a pretty big departure from other link type UIs. (It’s possible to upload within the asset selection modal though.)

@brandonkelly brandonkelly merged commit 12c6da1 into 5.3 Jul 1, 2024
@brandonkelly brandonkelly deleted the feature/cms-1279-link-field branch July 1, 2024 14:07
@alexanderbuergin
Copy link

alexanderbuergin commented Jul 25, 2024

Very Good news.

Will there be Support for Sites, Products (Craft Commerce) and Product Variants (Craft Commerce)?

Why not adding a Multi Type Field like an Entry Field? A Field Type in which every Type of Craft CMS native Element can be included? Like Entry, Category, Asset, Product, Site, Product Variant, and so on.?

@splendidrob
Copy link

Great news....really pleased to see this being added, should be a great addition! One question - will there be anything in place to help migrate data from some of those older systems such as linkfield etc into this? One of the big holdbacks we have to upgrading to Craft 5 is that some sites have all the link data in those plugins and an upgrade would mean going through the entire site and re-populating links...

@brandonkelly
Copy link
Member Author

@alexanderbuergin Yes to products; no to variants (variants don’t have URIs). I hadn’t considered sites but I guess they could be an option.

Why not adding a Multi Type Field like an Entry Field? A Field Type in which every Type of Craft CMS native Element can be included? Like Entry, Category, Asset, Product, Site, Product Variant, and so on.?

There is a small amount of work involved for each element type (defining settings, defining sources that should be available to select from, defining the selectable element criteria, etc.), so we can’t just add every element type automatically.

@splendidrob Good question. I’m guessing it will be possible to write a script for it. I’m not totally sure where that should live, besides probably not directly within Craft.

@mark-reason
Copy link

Is it possible to access the link label field via graphql?

@brandonkelly
Copy link
Member Author

@mark-reason It will be in Craft 5.6 (#16237).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.